﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading;

namespace WISBenchmarkClient
{
    class Haufigkeit
    {
        public readonly double[] Q = new double[6] { 5, 2, 5, 2, 2, 4 };

    }

    static class Program
    {
        private static int _numThreads = 5;
        private static int _waitTime = 1000;
        private static int _maxRequests = 100;
        private static readonly Deck Deck = new Deck();
        private static readonly List<Client> Clients = new List<Client>();
        private static int ReqNo { get; set; }
        private static int _actualFinish = 0;
        private static readonly object CurrLock = new object();
        private static readonly object CurrLock1 = new object();
        private static Dictionary<string, StatsProp> stats = new Dictionary<string, StatsProp>();

        static void Main()
        {
            string answer = "";
            Console.WriteLine("Bitte geben sie die Anzahl der Clients ein [5]: ");
            answer = Console.ReadLine();
            if (!string.IsNullOrEmpty(answer))
            {
                _numThreads = Convert.ToInt32(answer);
            }
            Console.WriteLine("Bitte geben sie die maximale Anzahl der Anfragen ein [100]: ");
            answer = Console.ReadLine();
            if (!string.IsNullOrEmpty(answer))
            {
                _maxRequests = Convert.ToInt32(answer);
            }
            Console.WriteLine("Bitte geben sie die durchschnittliche Wartezeit pro Client / Anfrage ein (in ms) [1000]: ");
            answer = Console.ReadLine();
            if (!string.IsNullOrEmpty(answer))
            {
                _waitTime = Convert.ToInt32(answer);
            }
            Console.WriteLine("Bitte geben sie prozentuale Werte für die Nutzung der verschiedenen Querys an mit \",\" getrennt [25,10,25,10,10,20]: ");
            answer = Console.ReadLine();
            var haufigkeit = new Haufigkeit();
            if (!string.IsNullOrEmpty(answer))
            {
                FindSmallestInt(answer, haufigkeit);
            }
            Deck.Query.Add("Home/Q1/2");
            Deck.Query.Add("Home/Q2/2");
            Deck.Query.Add("Home/Q3/342");
            Deck.Query.Add("Home/Q4/1");
            Deck.Query.Add("Home/Q5/2");
            Deck.Query.Add("Home/Q6/2");
            Deck.NewDeck(haufigkeit);



            ReqNo = 1;
            // Start der einzelnen Clients, in seperaten Threads
            for (var i = 0; i < _numThreads; i++)
            {
                var j = i;
                Clients.Add(new Client((x) => SendRequest(j, x), _waitTime));
            }
            foreach (Client c in Clients)
            {
                c.StartClient();
            }
            Console.WriteLine("All clients started");

        }

        // Umwandlung der Prozentwerte in möglichst kleine ganzzahlige Integerwerte
        private static void FindSmallestInt(string haufigkeitStr, Haufigkeit haufigkeit)
        {
            var haufigkeitPro = haufigkeitStr.Split(',');
            int i;
            for (i = 1; i <= 100; i++)
            {
                bool okay = true;
                for (int j = 0; j < 6; j++)
                {
                    haufigkeit.Q[j] = i * (Convert.ToDouble(haufigkeitPro[j]) / 100);
                    if (haufigkeit.Q[j].ToString(CultureInfo.InvariantCulture).Contains(".")) 
                        okay = false;
                }
                if (okay)
                    break;
            }
        }

        // Wird als Funktion den Clients mitgegeben und wird von allen Clients aufgerufen um eine Anfrage zu stellen
        private static Boolean SendRequest(int clientNumber, HttpClient client)
        {
            var ReqNo = 0;
            lock (CurrLock)
            {
                ReqNo = Program.ReqNo;
                Program.ReqNo++;
            }
            if (ReqNo > _maxRequests) return false;
            var url = Deck.GetCard();
            var startTime = DateTime.Now;
            var response = client.GetAsync(url).Result; // Blocking call!
            var stopTime = DateTime.Now;
            var elpTime = (stopTime - startTime).TotalMilliseconds;
            lock (CurrLock1)
            {
                if (stats.ContainsKey(url))
                {

                    stats[url].Counter = stats[url].Counter + 1;
                    stats[url].Time = stats[url].Time + elpTime;

                }
                else
                {
                    stats.Add(url, new StatsProp { Counter = 1, Time = elpTime });
                }
            }
            if (response.IsSuccessStatusCode)
            {
                Console.Write("Req {0} from {1} {2} Successful request", ReqNo, clientNumber, url);
            }
            else
            {
                Console.Write("Req {0} from {1} {2} {3} ({4})", ReqNo, clientNumber, url, (int)response.StatusCode, response.ReasonPhrase);
            }
            Console.WriteLine(" in {0} miliseconds", elpTime);
            lock (CurrLock)
            {
                _actualFinish++;
            }
            if (ReqNo == _maxRequests)
            {
                while (ReqNo != _actualFinish)
                {
                    Thread.Sleep(100);
                }
                (new Thread(printStat)).Start();
            }
            return true;


        }

        public static void printStat()
        {
            Console.WriteLine("Stats:");
            foreach (KeyValuePair<string, StatsProp> stat in stats)
            {
                Console.WriteLine(stat.Key + " Aufrufe: " + stat.Value.Counter + " Durchschnittszeit: " + stat.Value.Time / stat.Value.Counter);
            }
            Console.WriteLine("Bitte beliebige Taste zum Beenden drücken...");
            Console.ReadLine();
        }

        class StatsProp
        {
            public int Counter = 0;
            public double Time = 0;
        }
    }



}
